package com.zx.redis.reactive.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * <p>
 * description: redis工具类  <br>
 * create: 2024-08-20 15:59
 * </p>
 *
 * @author zhou  xun
 */
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
@RequiredArgsConstructor
public class RedisReactiveService {

    private final ReactiveRedisTemplate reactiveRedisTemplate;

    /**
     * 将指定的键值对存储到缓存中。
     *
     * @param key   要存储的键
     * @param value 要存储的值
     * @return 一个Mono<Void>，表示操作完成
     */
    public <T> Mono<Void> setCacheObject(String key, T value) {
        return reactiveRedisTemplate.opsForValue().set(key, value).then();
    }

    /**
     * 将指定的键值对和过期时间存储到缓存中。
     *
     * @param key     要存储的键
     * @param value   要存储的值
     * @param timeout 键的过期时间
     * @return 一个Mono<Void>，表示操作完成
     */
    public <T> Mono<Void> setCacheObject(String key, T value, Duration timeout) {
        return reactiveRedisTemplate.opsForValue().set(key, value, timeout).then();
    }

    /**
     * 设置指定键的过期时间。
     *
     * @param key     要设置过期时间的键
     * @param timeout 过期时间
     * @return 一个Mono<Boolean>，表示操作是否成功
     */
    public Mono<Boolean> expire(String key, Duration timeout) {
        return reactiveRedisTemplate.expire(key, timeout);
    }

    /**
     * 从缓存中获取指定键的值。
     *
     * @param key 要获取值的键
     * @return 一个Mono<T>，包含键对应的值
     */
    public <T> Mono<T> getCacheObject(String key) {
        return (Mono<T>) reactiveRedisTemplate.opsForValue().get(key);
    }

    /**
     * 获取缓存对象并转换为指定类型(响应式版本)
     *
     * @param key   缓存的键值
     * @param clazz 目标类型Class对象
     * @param <T>   泛型类型
     * @return Mono包装的指定类型的缓存对象，不存在则返回空Mono
     * @author zhou  xun
     * @since 2025-10-23
     */
    public <T> Mono<T> getCachedObjectReactive(String key, Class<T> clazz) {
        return reactiveRedisTemplate.opsForValue().get(key)
                .filter(Objects::nonNull)
                .flatMap(cacheObject ->
                        Mono.fromCallable(() -> {
                            // 直接类型匹配
                            if (clazz.isInstance(cacheObject)) {
                                return clazz.cast(cacheObject);
                            }
                            // JSONObject 转换
                            if (cacheObject instanceof JSONObject) {
                                return ((JSONObject) cacheObject).toJavaObject(clazz);
                            }
                            // 其他情况通过 JSON 序列化处理
                            String jsonString = JSON.toJSONString(cacheObject);
                            return JSON.parseObject(jsonString, clazz);
                        }).subscribeOn(Schedulers.boundedElastic())
                );
    }

    /**
     * 删除缓存中的指定键。
     *
     * @param key 要删除的键
     * @return 一个Mono<Boolean>，表示操作是否成功
     */
    public Mono<Boolean> deleteObject(String key) {
        return reactiveRedisTemplate.delete(key).hasElement();
    }

    /**
     * 批量删除缓存中的多个键。
     *
     * @param collection 要删除的键的集合
     * @return 一个Flux<Long>，表示每个键删除操作的结果
     */
    public Mono<Long> deleteObjects(Collection<String> collection) {
        return reactiveRedisTemplate.delete(collection);
    }

    /**
     * 将列表数据存储到缓存中。
     *
     * @param key      要存储的键
     * @param dataList 要存储的数据列表
     * @return 一个Flux<Void>，表示操作完成
     */
    public <T> Flux<Void> setCacheList(String key, List<T> dataList) {
        return Flux.fromIterable(dataList)
                .flatMap(data -> reactiveRedisTemplate.opsForList()
                        .rightPush(key, data).then());
    }

    /**
     * 从缓存中获取列表数据。
     *
     * @param key 要获取数据的键
     * @return 一个Flux<T>，包含键对应的列表数据
     */
    public <T> Flux<T> getCacheList(String key) {
        return (Flux<T>) reactiveRedisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 将集合数据存储到缓存中。
     *
     * @param key     要存储的键
     * @param dataSet 要存储的数据集合
     * @return 一个Mono<Void>，表示操作完成
     */
    public <T> Mono<Void> setCacheSet(String key, Set<T> dataSet) {
        return Flux.fromIterable(dataSet)
                .flatMap(data -> reactiveRedisTemplate.opsForSet()
                        .add(key, data)).then();
    }

    /**
     * 从缓存中获取集合数据。
     *
     * @param key 要获取数据的键
     * @return 一个Flux<T>，包含键对应的集合数据
     */
    public <T> Flux<T> getCacheSet(String key) {
        return (Flux<T>) reactiveRedisTemplate.opsForSet().members(key);
    }
}
